#![allow(dead_code)]

use std::usize;

#[derive(Debug, Clone, Copy)]
pub enum Variable {
    Given(f64),
    Unknown,
}

impl Variable {
    pub fn unwrap(&self) -> f64 {
        match self {
            Variable::Given(a) => *a,
            Variable::Unknown => panic!("Variable is unknown"),
        }
    }
}

impl PartialEq for Variable {
    fn eq(&self, other: &Self) -> bool {
        match (self, other) {
            (Variable::Given(a), Variable::Given(b)) => a == b,
            (Variable::Unknown, Variable::Unknown) => true,
            _ => false,
        }
    }
}
impl Eq for Variable {}

#[derive(Debug, Clone, Copy)]
pub struct Dot {
    pub x: f64,
    pub y: f64,
    pub prime: Variable,
    pub double_prime: Variable,
}

impl Dot {
    pub fn new(x: f64, y: f64) -> Self {
        Self { x, y, prime: Variable::Unknown, double_prime: Variable::Unknown }
    }

    fn give_prime(&mut self, prime: f64) {
        self.prime = Variable::Given(prime);
    }

    fn give_double_prime(&mut self, double_prime: f64) {
        self.double_prime = Variable::Given(double_prime);
    }
}

impl PartialEq for Dot {
    fn eq(&self, other: &Self) -> bool {
        self.x == other.x
    }
    
}

impl PartialOrd for Dot {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        self.x.partial_cmp(&other.x)
    }
}

impl Eq for Dot {}

impl Ord for Dot {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        if self.x.partial_cmp(&other.x) == None {
            return std::cmp::Ordering::Equal;
        }
        self.x.partial_cmp(&other.x).unwrap()
    }
}

pub struct Spline{
    pub dots: Vec<Dot>,
}

impl Spline {
    pub fn new(mut dots: Vec<Dot>) -> Self {
        dots.sort();
        Self { dots }
    }

    pub fn reset(&mut self, mut dots: Vec<Dot>) {
        dots.sort();
        self.dots = dots;
    }

    pub fn count_givens(&self) -> Vec<(usize, i32)> {
        let mut results: Vec<(usize, i32)> = Vec::new();
        for (i, dot) in self.dots.iter().enumerate() {
            if let Variable::Given(_) = dot.prime {
                results.push((i, 1));
            }
            if let Variable::Given(_) = dot.double_prime {
                results.push((i, 2));
            }
        }
        results
    }

    pub fn judge_solvable(&self) -> Result<usize, usize> {
        let givens = self.count_givens();
        if givens.len() == 2 {
            Ok(givens.len())
        } else {
            Err(givens.len())
        }
    }

    pub fn reset_givens(&mut self) {
        for dot in &mut self.dots {
            dot.prime = Variable::Unknown;
            dot.double_prime = Variable::Unknown;
        }
    }

    pub fn judge_complete(&self) -> Result<usize, usize> {
        let length = self.dots.len();
        if length >= 3 {
            return Ok(length);
        }
        Err(length)
    }

    pub fn give_left_prime(&mut self, prime: f64) -> Result<usize, usize> {
        let len = self.judge_complete();
        match len {
            Ok(len) => {
                self.dots[0].give_prime(prime);
                Ok(len)
            }
            Err(len) => Err(len),
        }
    }

    pub fn give_left_double_prime(&mut self, double_prime: f64) -> Result<usize, usize> {
        let len = self.judge_complete();
        match len {
            Ok(len) => {
                self.dots[0].give_double_prime(double_prime);
                Ok(len)
            }
            Err(len) => Err(len),
        }
    }

    pub fn give_right_prime(&mut self, prime: f64) -> Result<usize, usize> {
        let len = self.judge_complete();
        match len {
            Ok(len) => {
                self.dots[len - 1].give_prime(prime);
                Ok(len)
            }
            Err(len) => Err(len),
        }
    }

    pub fn give_right_double_prime(&mut self, double_prime: f64) -> Result<usize, usize> {
        let len = self.judge_complete();
        match len {
            Ok(len) => {
                self.dots[len - 1].give_double_prime(double_prime);
                Ok(len)
            }
            Err(len) => Err(len),
        }
    }

    pub fn get_matrix(&self) -> Result<(Vec<Vec<f64>>, Vec<f64>), usize> {
        let len = self.judge_solvable();
        match len {
            Ok(_) => {
                let additional_variable = self.count_givens();
                let dot_num = self.dots.len();
                let equation_num = (dot_num - 1) * 4;
                let mut right = vec![0.0; equation_num];
                let mut matrix = vec![vec![0.0; equation_num]; equation_num];
                for i in 0..(dot_num - 1){
                    right[2*i] = self.dots[i].y;
                    matrix[2*i][4*i] = 1.0;
                    matrix[2*i][4*i+1] = self.dots[i].x;
                    matrix[2*i][4*i+2] = self.dots[i].x.powi(2);
                    matrix[2*i][4*i+3] = self.dots[i].x.powi(3);
                    right[2*i+1] = self.dots[i+1].y;
                    matrix[2*i+1][4*i] = 1.0;
                    matrix[2*i+1][4*i+1] = self.dots[i+1].x;
                    matrix[2*i+1][4*i+2] = self.dots[i+1].x.powi(2);
                    matrix[2*i+1][4*i+3] = self.dots[i+1].x.powi(3);
                }
                for i in 1..(dot_num - 1){
                    right[2*dot_num-4+2*i] = 0.0;
                    matrix[2*dot_num-4+2*i][4*i -4] = 0.0;
                    matrix[2*dot_num-4+2*i][4*i -3] = 1.0;
                    matrix[2*dot_num-4+2*i][4*i -2] = 2.0 * self.dots[i].x;
                    matrix[2*dot_num-4+2*i][4*i -1] = 3.0 * self.dots[i].x.powi(2);
                    matrix[2*dot_num-4+2*i][4*i] = 0.0;
                    matrix[2*dot_num-4+2*i][4*i +1] = -1.0;
                    matrix[2*dot_num-4+2*i][4*i +2] = -2.0 * self.dots[i].x;
                    matrix[2*dot_num-4+2*i][4*i +3] = -3.0 * self.dots[i].x.powi(2);
                    right[2*dot_num-4+2*i+1] = 0.0;
                    matrix[2*dot_num-4+2*i+1][4*i -4] = 0.0;
                    matrix[2*dot_num-4+2*i+1][4*i -3] = 0.0;
                    matrix[2*dot_num-4+2*i+1][4*i -2] = 2.0;
                    matrix[2*dot_num-4+2*i+1][4*i -1] = 6.0 * self.dots[i].x;
                    matrix[2*dot_num-4+2*i+1][4*i] = 0.0;
                    matrix[2*dot_num-4+2*i+1][4*i +1] = 0.0;
                    matrix[2*dot_num-4+2*i+1][4*i +2] = -2.0;
                    matrix[2*dot_num-4+2*i+1][4*i +3] = -6.0 * self.dots[i].x;
                }
                let addtional_equation_1_dot_index = additional_variable[0].0;
                match additional_variable[0].1 {
                    1 => {
                        right[equation_num - 2] = self.dots[addtional_equation_1_dot_index].prime.unwrap();
                        if addtional_equation_1_dot_index == 0 {
                            matrix[equation_num - 2][0] = 0.0;
                            matrix[equation_num - 2][1] = 1.0;
                            matrix[equation_num - 2][2] = 2.0 * self.dots[addtional_equation_1_dot_index].x;
                            matrix[equation_num - 2][3] = 3.0 * self.dots[addtional_equation_1_dot_index].x.powi(2);
                        } else {
                            matrix[equation_num - 2][4*addtional_equation_1_dot_index - 4] = 0.0;
                            matrix[equation_num - 2][4*addtional_equation_1_dot_index - 3] = 1.0;
                            matrix[equation_num - 2][4*addtional_equation_1_dot_index - 2] = 2.0 * self.dots[addtional_equation_1_dot_index].x;
                            matrix[equation_num - 2][4*addtional_equation_1_dot_index - 1] = 3.0 * self.dots[addtional_equation_1_dot_index].x.powi(2);
                        }
                    }
                    2 => {
                        right[equation_num - 2] = self.dots[addtional_equation_1_dot_index].double_prime.unwrap();
                        if addtional_equation_1_dot_index == 0 {
                            matrix[equation_num - 2][0] = 0.0;
                            matrix[equation_num - 2][1] = 0.0;
                            matrix[equation_num - 2][2] = 2.0;
                            matrix[equation_num - 2][3] = 6.0 * self.dots[addtional_equation_1_dot_index].x;
                        } else {
                            matrix[equation_num - 2][4*addtional_equation_1_dot_index - 4] = 0.0;
                            matrix[equation_num - 2][4*addtional_equation_1_dot_index - 3] = 0.0;
                            matrix[equation_num - 2][4*addtional_equation_1_dot_index - 2] = 2.0;
                            matrix[equation_num - 2][4*addtional_equation_1_dot_index - 1] = 6.0 * self.dots[addtional_equation_1_dot_index].x;
                        }
                    }
                    _ => panic!("Error"),
                }
                let addtional_equation_2_dot_index = additional_variable[1].0;
                match additional_variable[1].1 {
                    1 => {
                        right[equation_num - 1] = self.dots[addtional_equation_2_dot_index].prime.unwrap();
                        if addtional_equation_2_dot_index == 0 {
                            matrix[equation_num - 1][0] = 0.0;
                            matrix[equation_num - 1][1] = 1.0;
                            matrix[equation_num - 1][2] = 2.0 * self.dots[addtional_equation_2_dot_index].x;
                            matrix[equation_num - 1][3] = 3.0 * self.dots[addtional_equation_2_dot_index].x.powi(2);
                        } else {
                            matrix[equation_num - 1][4*addtional_equation_2_dot_index - 4] = 0.0;
                            matrix[equation_num - 1][4*addtional_equation_2_dot_index - 3] = 1.0;
                            matrix[equation_num - 1][4*addtional_equation_2_dot_index - 2] = 2.0 * self.dots[addtional_equation_2_dot_index].x;
                            matrix[equation_num - 1][4*addtional_equation_2_dot_index - 1] = 3.0 * self.dots[addtional_equation_2_dot_index].x.powi(2);
                        }
                    }
                    2 => {
                        right[equation_num - 1] = self.dots[addtional_equation_2_dot_index].double_prime.unwrap();
                        if addtional_equation_2_dot_index == 0 {
                            matrix[equation_num - 1][0] = 0.0;
                            matrix[equation_num - 1][1] = 0.0;
                            matrix[equation_num - 1][2] = 2.0;
                            matrix[equation_num - 1][3] = 6.0 * self.dots[addtional_equation_2_dot_index].x;
                        } else {
                            matrix[equation_num - 1][4*addtional_equation_2_dot_index - 4] = 0.0;
                            matrix[equation_num - 1][4*addtional_equation_2_dot_index - 3] = 0.0;
                            matrix[equation_num - 1][4*addtional_equation_2_dot_index - 2] = 2.0;
                            matrix[equation_num - 1][4*addtional_equation_2_dot_index - 1] = 6.0 * self.dots[addtional_equation_2_dot_index].x;
                        }
                    }
                    _ => panic!("Error"),
                }
                Ok((matrix, right))
            }
            Err(len) => return Err(len),
        }
    }
}